﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Wyse.Server.Base;
using Wyse.Server.Logging;

namespace Wyse.Server.Game
{
    /// <summary>
    /// Manages the state of a single game
    /// </summary>
    public class GameManager
    {
        /// <summary>
        /// Creates a new game manager
        /// </summary>
        /// <param name="tableName"></param>
        public GameManager(String tableName, GameServerModule server)
        {
            TableName = tableName;
            mServer = server;

            // initialize action handler mappings

            mActionHandlers[ClientActions.Hello] = OnHello;
        }

        /// <summary>
        /// Gets the name of the current table
        /// </summary>
        public String TableName
        {
            get;
            private set;
        }

        /// <summary>
        /// Processes a message from the specified client
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="client"></param>
        public void ProcessClientMessage(Message msg, GameClientContext client)
        {
            ProcessMessageState state = new ProcessMessageState()
            {
                Message = msg,
                Client = client
            };

            ThreadPool.QueueUserWorkItem(ProcessMessageTask, state);
        }

        /// <summary>
        /// Message processing task function
        /// </summary>
        /// <param name="param"></param>
        private void ProcessMessageTask(Object param)
        {
            ProcessMessageState state = (ProcessMessageState)param;
            Message msg = state.Message;
            GameClientContext client = state.Client;

            lock (this)
            {
                Action<Message, GameClientContext> handler;
                if (mActionHandlers.TryGetValue(msg.Action, out handler))
                    handler(msg, client);
                else
                    client.SendFailMessage(FailCodes.InvalidMessage);
            }
        }

        /// <summary>
        /// Message handler for Hello messages
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="cx"></param>
        private void OnHello(Message msg, GameClientContext cx)
        {
            if (mState != GameState.WaitingForPlayers)
            {
                cx.SendFailMessage(FailCodes.InvalidState);
                return;
            }

            if (!msg.HasParametersOfType<ClientActions.HelloParams>())
            {
                cx.SendFailMessage(FailCodes.InvalidMessage);
                return;
            }

            var p = msg.GetParameters<ClientActions.HelloParams>();
            p.Name = p.Name.Trim();

            if (!IsValidName(p.Name))
            {
                cx.SendFailMessage(FailCodes.InvalidName);
                return;
            }

            PlayerSex sex;
            if(p.Sex == 'M')
                sex = PlayerSex.Male;
            else if(p.Sex == 'F')
                sex = PlayerSex.Female;
            else
                sex = PlayerSex.Unknown;

            PlayerState player = new PlayerState(p.Name, sex);
            player.Money = p.Funds;

            cx.PlayerState = player;

            var token = mServer.GeneratePlayerToken(player, this);

            // send the welcome message
            cx.SendMessage(new Message(ServerActions.Welcome, new ServerActions.WelcomeParams()
            {
                Token = token.ID.ToString()
            }));

            Log.Info("Player {0} joined the game on table {1}", player.Name, TableName);
        }

        /// <summary>
        /// Checks if the specified string is a valid player name
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        private bool IsValidName(String name)
        {
            if (String.IsNullOrWhiteSpace(name))
                return false;
            for (int i = 0; i < name.Length; ++i)
            {
                if (!Char.IsLetterOrDigit(name, i) && !Char.IsWhiteSpace(name, i) && name[i] != '_')
                    return false;
            }

            if (mPlayers.ContainsKey(name))
                return false;

            return true;
        }

        /// <summary>
        /// State passed to message processing tasks
        /// </summary>
        private class ProcessMessageState
        {
            public Message Message;
            public GameClientContext Client;
        }

        /// <summary>
        /// Game states
        /// </summary>
        private enum GameState
        {
            WaitingForPlayers,
            InProgress,
            Ended
        }

        private Dictionary<String, GameClientContext> mPlayers = new Dictionary<string, GameClientContext>();
        private Dictionary<String, Action<Message, GameClientContext>> mActionHandlers = new Dictionary<string, Action<Message, GameClientContext>>();
        private GameServerModule mServer;
        private GameState mState = GameState.WaitingForPlayers;
    }
}
